package com.lsl.rdf.basics;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ReflectUtil;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 枚举对象容器工具类
 * <p>
 * 通过对枚举类内容进行解析和缓存
 * Created by lsl on 2021/4/26.
 */
public abstract class EnumContainer {
    /**
     * 枚举类，枚举值与枚举关系容器
     */
    private static final Map<String, Map<Integer, BasicsEnum>> clazzEnumValueMap = new ConcurrentHashMap<>(32);
    /**
     * 枚举类，枚举描述与枚举关系容器
     */
    private static final Map<String, Map<String, BasicsEnum>> clazzEnumDescMap = new ConcurrentHashMap<>(32);
    /**
     * 枚举类，枚举码与枚举关系容器
     */
    private static final Map<String, Map<String, BasicsEnum>> clazzEnumCodeMap = new ConcurrentHashMap<>(32);

    private static final Byte TYPE_VAL = 1;
    private static final Byte TYPE_DESC = 2;
    private static final Byte TYPE_CODE = 3;

    /**
     * 获取指定枚举类对应值与枚举关系对象,如果不存在则对子类对象进行初始化
     *
     * @param clazz 枚举子类
     * @return map
     */
    public static Map<Integer, BasicsEnum> getClazzEnumValueMap(Class<? extends BasicsEnum> clazz) {
        if (Objects.isNull(clazz)) {
            return null;
        }
        Map<Integer, BasicsEnum> enumMap = clazzEnumValueMap.get(clazz.getName());
        if (Objects.isNull(enumMap)) {
            enumMap = initializeEnumMap(clazz, TYPE_VAL);
        }
        return enumMap;
    }

    /**
     * 获取指定枚举类对应描述与枚举关系对象,如果不存在则对子类对象进行初始化
     *
     * @param clazz 枚举子类
     * @return map
     */
    public static Map<String, BasicsEnum> getClazzEnumDescMap(Class<? extends BasicsEnum> clazz) {
        if (Objects.isNull(clazz)) {
            return null;
        }
        Map<String, BasicsEnum> enumMap = clazzEnumDescMap.get(clazz.getName());
        if (Objects.isNull(enumMap)) {
            enumMap = initializeEnumMap(clazz, TYPE_DESC);
        }
        return enumMap;
    }

    /**
     * 获取指定枚举类对应码与枚举关系对象,如果不存在则对子类对象进行初始化
     *
     * @param clazz 枚举子类
     * @return map
     */
    public static Map<String, BasicsEnum> getClazzEnumCodeMap(Class<? extends BasicsEnum> clazz) {
        if (Objects.isNull(clazz)) {
            return null;
        }
        Map<String, BasicsEnum> enumMap = clazzEnumCodeMap.get(clazz.getName());
        if (Objects.isNull(enumMap)) {
            enumMap = initializeEnumMap(clazz, TYPE_CODE);
        }
        return enumMap;
    }

    /**
     * 初始化类的枚举值，初始化完成后将不会重新初始化，但多线程情况下可能会出现重复初始化，但是对结果本身不会产生影响
     *
     * @param clazz 被初始化枚举子类对象class
     * @param type  初始化返回类型，1 Integer->enums, 2 String->enums
     * @param <T>   泛型
     * @return map
     */
    @SuppressWarnings({"unchecked"})
    protected static <T> Map<T, BasicsEnum> initializeEnumMap(Class<?> clazz, Byte type) {
        if (Objects.isNull(clazz)) {
            return null;
        }
        String clazzName = clazz.getName();
        Map<?, BasicsEnum> enumMap;
        if (TYPE_VAL.equals(type)) {
            enumMap = clazzEnumValueMap.get(clazzName);
        } else if (TYPE_DESC.equals(type)) {
            enumMap = clazzEnumDescMap.get(clazzName);
        } else if (TYPE_CODE.equals(type)) {
            enumMap = clazzEnumCodeMap.get(clazzName);
        } else {
            return null;
        }
        if (Objects.nonNull(enumMap)) {
            return (Map<T, BasicsEnum>) enumMap;
        }
        Map<Integer, BasicsEnum> enumValueMap = new HashMap<>();
        Map<String, BasicsEnum> enumDescMap = new HashMap<>();
        Map<String, BasicsEnum> enumCodeMap = new HashMap<>();
        Field[] fields = ReflectUtil.getFieldsDirectly(clazz, false);
        for (Field field : fields) {
            Object value = ReflectUtil.getFieldValue(BasicsEnum.class, field);
            if (value instanceof BasicsEnum) {
                BasicsEnum baseEnum = (BasicsEnum) value;
                Integer val = baseEnum.getVal();
                String desc = baseEnum.getDesc();
                String code = baseEnum.getCode();
                if (Objects.nonNull(val)) {
                    enumValueMap.put(val, baseEnum);
                }
                if (Objects.nonNull(desc)) {
                    enumDescMap.put(desc, baseEnum);
                }
                if (Objects.nonNull(code)) {
                    enumCodeMap.put(code, baseEnum);
                }
            }
        }
        if (CollectionUtil.isNotEmpty(enumValueMap)) {
            clazzEnumValueMap.put(clazzName, enumValueMap);
        }
        if (CollectionUtil.isNotEmpty(enumDescMap)) {
            clazzEnumDescMap.put(clazzName, enumDescMap);
        }
        if (CollectionUtil.isNotEmpty(enumCodeMap)) {
            clazzEnumCodeMap.put(clazzName, enumCodeMap);
        }
        return (Map<T, BasicsEnum>) (TYPE_VAL.equals(type) ? enumValueMap : TYPE_DESC.equals(type) ? enumDescMap : enumCodeMap);
    }
}
